/*=============================================================
* 	Copyright (C) 2024 USTC All rights reserved.
* 	
* 	filename : common.h
* 	Author: Wei Jinsong
* 	Mail: vitus@mainl.ustc.edu.cn
* 	Date 2024-05-13
* 	Abstract 
* 		
*
=============================================================*/

#ifndef __COMMON_H__
#define __COMMON_H__

#include <queue>
#include "systemc.h"
#include "tlm.h"
#include "tlm_utils/peq_with_get.h"

using namespace sc_core;
using namespace std;

struct SpikePayload
{
    bool valid=1;

    uint32_t srcid; // source node id
    uint32_t srcneuid; // source neuron id

    uint32_t tgtid; // target node id
    uint32_t tgtneuid;

    int32_t channel;
    int32_t height;
    int32_t width;

    bool  switch_phase = 0;
    int32_t step = 0;
    int32_t phase = 0;

    double value=1.0;
    void *weights;
};

template <typename TRANS = SpikePayload>
class SpikeInterface : public virtual sc_core::sc_interface
{
public:
    virtual bool spike_transport_fw(TRANS& trans, sc_core::sc_time &delay)=0;
};

class TimerInterface : public virtual sc_core::sc_interface
{
public:
    virtual int get_step() = 0;
    virtual int get_phase() = 0;
    virtual bool set_tick() = 0;
};

template<class PAYLOAD>
class SpikeEventQueue : public::sc_core::sc_object 
{
public:

public:
  typedef PAYLOAD transaction_type;
  typedef std::pair<const sc_core::sc_time, transaction_type*> pair_type;

public:
    SpikeEventQueue(const char* name)
        : sc_core::sc_object(name)
    {}

    void notify(transaction_type& trans, const sc_core::sc_time& t)
    {
        m_scheduled_events.insert(pair_type(t + sc_core::sc_time_stamp(), &trans));
        m_event.notify(t);
    }

    void notify(transaction_type& trans)
    {
        m_scheduled_events.insert(pair_type(sc_core::sc_time_stamp(), &trans));
        m_event.notify(); // immediate notification
    }

    // needs to be called until it returns 0
    transaction_type* get_next_transaction()
    {
        if (m_scheduled_events.empty()) {
            return 0;
        }

        sc_core::sc_time now = sc_core::sc_time_stamp();
        if (m_scheduled_events.begin()->first <= now) {
            transaction_type* trans = m_scheduled_events.begin()->second;
            m_scheduled_events.erase(m_scheduled_events.begin());
            return trans;
        }

        m_event.notify(m_scheduled_events.begin()->first - now);

        return 0;
    }

    sc_core::sc_event& get_event()
    {
        return m_event;
    }

    // Cancel all events from the event queue
    void cancel_all() {
        m_scheduled_events.clear();
        m_event.cancel();
    }

    uint32_t event_num() const
    {
        return m_scheduled_events.size();
    }

private:
    std::multimap<const sc_core::sc_time, transaction_type*> m_scheduled_events;
    sc_core::sc_event m_event;
};

template <class T>
class SpikeFifo : public::sc_fifo<T>
{
public:
    SpikeFifo(uint32_t D=16)
        :sc_fifo<T>(D)
    {
    }
    T& front()
    {
        return (sc_fifo<T>::m_buf[sc_fifo<T>::m_ri]);
    }
private:
};

template <typename T>
class SynapseInterface : public virtual sc_core::sc_interface
{
public:
    virtual void set_params(string file) = 0;
    virtual vector<T*> get_axon(T*) = 0;
    virtual vector<T*> get_dendrite(T*) =0;
};

#endif

